/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004-2005  Benoit Blanchon                      */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/


/******************************************************************/
/* Build configuration                                            */
/******************************************************************/

#define	TRACE_LEVEL	2


/******************************************************************/
/* Includes                                                       */
/******************************************************************/

// module's interface
#include "HookReg.h"

// standard headers
#include <windows.h>
#include <tchar.h>

// project's headers
#include "Assert.h"
#include "Hooks.h"
#include "SpyDllInt.h"
#include "Strlcpy.h"
#include "Trace.h"


/******************************************************************/
/* Internal macros                                                */
/******************************************************************/

//#define SHARED __attribute__((section(".shr"), shared))


/******************************************************************/
/* Internal constants                                             */
/******************************************************************/

// this module maintain a list of all open HKEY
// the following constant set the maximun open keys
#define MAX_OPEN_KEYS		32

#define HANDLES_PER_KEY		128

#define HIWAT_LEVEL		(HANDLES_PER_KEY*3/4)

#define LOWAT_LEVEL		(HANDLES_PER_KEY/2)

#define MAX_KEY_PATH		1024


static const TCHAR g_szFileMappingName[] = TEXT("WinpoochRegKeysData") ;

static const TCHAR g_szMutexName[] = TEXT("WinpoochRegKeysMutex") ;


/******************************************************************/
/* Internal data types                                            */
/******************************************************************/

typedef struct {
  HKEY		aKeys[HANDLES_PER_KEY] ;
  TCHAR		szPath[MAX_KEY_PATH] ;
} REGKEYNAME ;

typedef struct {
  UINT		nKeys ;
  REGKEYNAME	pKeys[MAX_OPEN_KEYS] ;
  BOOL		bFull ; // TRUE if high water level reached
} SHAREDDATA ;


/******************************************************************/
/* Internal data                                                  */
/******************************************************************/

// named mutex to acces to the shared data
static HANDLE g_hSharedData = NULL ;

// pointer to the shared data
static SHAREDDATA * g_pSharedData = NULL ;

// named mutex to acces to the shared data
static HANDLE g_hMutex = NULL ;


/******************************************************************/
/* Internal functions                                             */
/******************************************************************/

// insert a key in key pool
BOOL	_HookReg_AddKey (HKEY hKey, LPCTSTR szName) ;

// remove a key from the key pool
VOID	_HookReg_RemKey (HKEY hKey) ;

// remove ending slashes
VOID	_HookReg_TrimKey (LPTSTR szKeyPath) ;

// ask the server if the key can be discarded
BOOL	_HookReg_CanDiscard (LPCTSTR szPath) ;

// get the key path from the pool
// or NULL if not found
LPCTSTR _HookReg_GetKeyPath (HKEY hKey) ;

VOID	_HookReg_LockMutex () ;

VOID	_HookReg_UnlockMutex () ;


/******************************************************************/
/* Exported function : Init                                       */
/******************************************************************/

BOOL HookReg_Init ()
{
  TRACE ;

  // create or open mutex
  g_hMutex = CreateMutex (NULL, FALSE, g_szMutexName) ;

  // ok ?
  if( ! g_hMutex ) 
    {
      if( ERROR_ACCESS_DENIED==GetLastError() )
	{
	  g_hMutex = OpenMutex (MUTEX_ALL_ACCESS, FALSE, g_szMutexName) ;

	  TRACE_ERROR (TEXT("OpenMutex failed (error=%u)\n"), GetLastError()) ;
	}
      else
	{
	  TRACE_ERROR (TEXT("CreateMutex failed (error=%u)\n"), GetLastError()) ;
	  goto failed ;
	}
    }

  TRACE_INFO (TEXT("Shared data size = %d kB\n"), sizeof(SHAREDDATA)>>10) ;

  // create file mapping
  g_hSharedData = CreateFileMapping (INVALID_HANDLE_VALUE,
				     NULL,
				     PAGE_READWRITE,
				     0, sizeof(SHAREDDATA),
				     g_szFileMappingName) ;
  if( ! g_hSharedData ) {
    TRACE_ERROR (TEXT("CreateFileMapping failed (error=%u)\n"), GetLastError()) ;
    goto failed ;
  }

  // map shared memory
  g_pSharedData = (SHAREDDATA*)MapViewOfFile (g_hSharedData,FILE_MAP_ALL_ACCESS,0,0,0) ;

  if( ! g_pSharedData ) {
    TRACE_ERROR (TEXT("MapViewOfFile failed (error=%u)\n"), GetLastError()) ;
    goto failed ;
  }

  // need to initialize shared data ?
  if( GetLastError()!=ERROR_ALREADY_EXISTS )
    { 
      _HookReg_LockMutex () ;

      // initialize shared data
      g_pSharedData->nKeys = 0 ;

      // add first keys
      _HookReg_AddKey (HKEY_CLASSES_ROOT,	TEXT("HKCR")) ;
      _HookReg_AddKey (HKEY_CURRENT_CONFIG,	TEXT("HKCC")) ;
      _HookReg_AddKey (HKEY_CURRENT_USER,	TEXT("HKCU")) ;
      _HookReg_AddKey (HKEY_DYN_DATA,		TEXT("HKDD")) ;
      _HookReg_AddKey (HKEY_LOCAL_MACHINE,	TEXT("HKLM")) ;
      _HookReg_AddKey (HKEY_PERFORMANCE_DATA,	TEXT("HKPD")) ;
      //_HookReg_AddKey (pData, HKEY_PERFORMANCE_NLSTEXT,	TEXT("HKPN")) ;
      //_HookReg_AddKey (pData, HKEY_PERFORMANCE_TEXT,	TEXT("HKPT")) ;
      _HookReg_AddKey (HKEY_USERS,		TEXT("HKU")) ;
	  
      _HookReg_UnlockMutex () ;      
    }
  
  return TRUE ;

 failed:

  CloseHandle (g_hSharedData) ;
  CloseHandle (g_hMutex) ;

  return FALSE ;
}


/******************************************************************/
/* Exported function : Uninit                                     */
/******************************************************************/

BOOL HookReg_Uninit ()
{
  TRACE ;

  // unmap shared data
  UnmapViewOfFile (g_pSharedData) ;

  // free shared data
  CloseHandle (g_hSharedData) ;

  CloseHandle (g_hMutex) ;

  return TRUE ;
}


/******************************************************************/
/* Internal function : TrimKey                                    */
/******************************************************************/

VOID	_HookReg_TrimKey (LPTSTR szKeyPath) 
{
  UINT	nLen ;

  ASSERT (szKeyPath!=NULL) ;

  nLen = _tcslen (szKeyPath) ;
  while( nLen>0 && szKeyPath[nLen-1]==TEXT('\\') )
    szKeyPath[--nLen] = 0 ;
}


/******************************************************************/
/* Internal function : CanDiscard                                 */
/******************************************************************/

BOOL _HookReg_CanDiscard (LPCTSTR szPath)
{
  SDNNEWREGKEY msg ;

  TRACE_INFO (TEXT("Key = %s\n"), szPath) ;

  // verify params
  ASSERT (szPath!=NULL) ;

  // fill message
  msg.hdr.dwCode	= SDN_NEW_REGISTRY_KEY ;
  msg.hdr.dwProcessId	= 0 ;
  _tcslcpy (msg.szKey, szPath, MAX_PATH) ;

  // send message to server
  return SpyInt_NotifyServer ((SDNMHDR*)&msg, sizeof(msg))!=1 ;
}



/******************************************************************/
/* Internal function : LockMutex                                  */
/******************************************************************/

VOID _HookReg_LockMutex () 
{
  ASSERT (g_hMutex!=NULL) ;

  DWORD dwWaitResult = WaitForSingleObject (g_hMutex, 5000) ;

  if( dwWaitResult!=WAIT_OBJECT_0 ) {
    TRACE_ERROR (TEXT("Mutex error %u"), dwWaitResult) ;
    return ;
  }
}


/******************************************************************/
/* Internal function : UnlockMutex                                */
/******************************************************************/

VOID _HookReg_UnlockMutex () 
{
  ASSERT (g_hMutex!=NULL) ;

  ReleaseMutex(g_hMutex) ;
}


/******************************************************************/
/* Internal function : GetKeyPath                                 */
/******************************************************************/

LPCTSTR _HookReg_GetKeyPath (HKEY hKey)
{
  int i, j ;
  LPCTSTR szPath = NULL ;

  TRACE_INFO (TEXT("Key = 0x%08X\n"), (DWORD)hKey) ;

  _HookReg_LockMutex () ;
  
  for( i=0 ; i<g_pSharedData->nKeys && !szPath  ; i++ )
    for( j=0 ; j<HANDLES_PER_KEY && !szPath ; j++ ) 
      if( g_pSharedData->pKeys[i].aKeys[j] == hKey ) {
	szPath = g_pSharedData->pKeys[i].szPath ;
	break ;
      }

  _HookReg_UnlockMutex () ;

  TRACE_INFO (TEXT("0x%08X : %s\n"), (DWORD)hKey, szPath) ;

  return szPath ;
}


/******************************************************************/
/* Internal function : AddKey                                     */
/******************************************************************/

BOOL _HookReg_AddKey (HKEY hKey, LPCTSTR szPath)
{
  int	iKey, iSlot ;
  BOOL	bFreeSlot, bHiwat, bLowat ;

  TRACE_INFO (TEXT("0x%08X : %s\n"), (DWORD)hKey, szPath) ;

  ASSERT (szPath) ;

  // verify that path isn't too long
  if( _tcslen(szPath)+1 >= MAX_PATH ) {
    TRACE_ERROR (TEXT("Path too long\n")) ;
    return FALSE ;
  }

  // try to remove HKEY, so as to avoid keeping old HKEY
  _HookReg_RemKey (hKey) ;

  _HookReg_LockMutex () ;

  // look for a free existing slot in the list
  bFreeSlot = FALSE ;
  for( iKey=0 ; iKey<g_pSharedData->nKeys ; iKey++ )
    {
      // skip unknown key
      if( g_pSharedData->pKeys[iKey].szPath[0]==TEXT('?') )
	continue ;

      // look for this key path in the list
      if( !_tcsicmp(g_pSharedData->pKeys[iKey].szPath, szPath) )
	{
	  // is there a free slot ?
	  for( iSlot=0 ; iSlot<HANDLES_PER_KEY ; iSlot++ )
	    {
	      if( g_pSharedData->pKeys[iKey].aKeys[iSlot]==hKey )
		{
		  _HookReg_UnlockMutex () ;
		  return TRUE ;
		}

	      if( !g_pSharedData->pKeys[iKey].aKeys[iSlot] )
		{
		  g_pSharedData->pKeys[iKey].aKeys[iSlot] = hKey ;
		  _HookReg_UnlockMutex () ;
		  return TRUE ;
		}
	    }
	  
	  TRACE_WARNING (TEXT("All slot filled for key %s\n"), szPath) ;
	}
    }

  // verify space in list
  if( g_pSharedData->nKeys >= MAX_OPEN_KEYS )  
    {
      TRACE_ERROR (TEXT("Max openned keys reached\n")) ;   
      _HookReg_UnlockMutex () ;
      return FALSE ;
    }
  
  // save key info
  memset (g_pSharedData->pKeys[g_pSharedData->nKeys].aKeys, 
	  0, HANDLES_PER_KEY*sizeof(HKEY)) ;
  g_pSharedData->pKeys[g_pSharedData->nKeys].aKeys[0] = hKey ;
  _tcslcpy (g_pSharedData->pKeys[g_pSharedData->nKeys].szPath, szPath, MAX_KEY_PATH) ;
  
  g_pSharedData->nKeys++ ;
  ASSERT (g_pSharedData->nKeys<=MAX_OPEN_KEYS) ;
  
  if( ! g_pSharedData->bFull )
    {
      bHiwat = g_pSharedData->nKeys>=HIWAT_LEVEL ;
      bLowat = FALSE ;
      if( bHiwat ) g_pSharedData->bFull = TRUE ;
    }
  else
    {
      bHiwat = FALSE ;
      bLowat = g_pSharedData->nKeys<=LOWAT_LEVEL ;
      if( bLowat ) g_pSharedData->bFull = FALSE ;
    }

  _HookReg_UnlockMutex () ;

  if( bHiwat ) {
    SDNMHDR msg = { SDN_KEY_POOL_HIWAT, GetCurrentProcessId () } ;
    SpyInt_NotifyServer (&msg, sizeof(msg)) ;
  }
  
  if( bLowat ) {
    SDNMHDR msg = { SDN_KEY_POOL_LOWAT, GetCurrentProcessId () } ;
    SpyInt_NotifyServer (&msg, sizeof(msg)) ;
  } 

  return TRUE ;
}


/******************************************************************/
/* Internal function : RemKey                                     */
/******************************************************************/

VOID _HookReg_RemKey (HKEY hKey)
{
  int	iKey, iSlot ;
  BOOL	bRemove ;

  TRACE_INFO (TEXT("Key = %08X\n"), hKey) ;

  if( (DWORD)hKey & 0x80000000 ) 
    { 
      TRACE_INFO (TEXT("Process trys to remove key 0x%lX (%s)\n"), 
		  (DWORD)hKey, _HookReg_GetKeyPath(hKey)) ;
      return ;
    }

  _HookReg_LockMutex () ;
  
  for( iKey=0 ; iKey<g_pSharedData->nKeys ; iKey++ ) 
    for( iSlot=0 ; iSlot<HANDLES_PER_KEY ; iSlot++ ) 
      if( g_pSharedData->pKeys[iKey].aKeys[iSlot] == hKey )
	goto found ;
  

  _HookReg_UnlockMutex () ; 

  return ;

 found:

  g_pSharedData->pKeys[iKey].aKeys[iSlot] = NULL ;
  
  bRemove = TRUE ;
  for( iSlot=0 ; iSlot<HANDLES_PER_KEY && bRemove ; iSlot++ )
    if( g_pSharedData->pKeys[iKey].aKeys[iSlot] ) 
      bRemove = FALSE ;
 
  if( bRemove )
    {
      g_pSharedData->nKeys-- ;
      memcpy (g_pSharedData->pKeys[iKey].aKeys,
	      g_pSharedData->pKeys[g_pSharedData->nKeys].aKeys,
	      HANDLES_PER_KEY*sizeof(HKEY)) ;
      _tcscpy (g_pSharedData->pKeys[iKey].szPath, 
	       g_pSharedData->pKeys[g_pSharedData->nKeys].szPath) ; 
    }

  _HookReg_UnlockMutex () ;
}



/******************************************************************/
/* Exported function : RegCreateKeyExA                            */
/******************************************************************/

LONG WINAPI Hook_RegCreateKeyExA (HKEY hKey,
				  LPCSTR lpSubKey,
				  DWORD Reserved,
				  LPSTR lpClass,
				  DWORD dwOptions,
				  REGSAM samDesired,
				  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
				  PHKEY phkResult,
				  LPDWORD lpdwDisposition)
{
  LPCTSTR	szParentKey ;
  TCHAR		szKeyName[MAX_KEY_PATH] ;
  LONG		lResult ;
  PROC		pfnStub = Hooks_GetStubAddress (HOOKS_REGCREATEKEYEXA) ;

  TRACE ;

  // try to get the full path of this key
  szParentKey = _HookReg_GetKeyPath(hKey) ;
  if( szParentKey )
    {
      wsprintf (szKeyName, TEXT("%s\\%hs"), szParentKey, lpSubKey) ;
      _HookReg_TrimKey (szKeyName) ;
      
      // too many open keys ?
      if( g_pSharedData->nKeys>=MAX_OPEN_KEYS ) {
	TRACE_ERROR (TEXT("Too many open keys, can't open the key \"%s\"\n"),  szKeyName) ;
	return ERROR_TOO_MANY_OPEN_FILES ;
      }
    }
  
  // call real RegCreateKeyExA
  lResult = (LONG) pfnStub (hKey, lpSubKey, Reserved, lpClass,
			    dwOptions, samDesired, lpSecurityAttributes,
			    phkResult, lpdwDisposition) ;

  if( ERROR_SUCCESS==lResult && szParentKey!=NULL ) 
    if( ! _HookReg_CanDiscard(szKeyName) )
      _HookReg_AddKey (*phkResult, szKeyName) ;

  return lResult ;
}


/******************************************************************/
/* Exported function : RegCreateKeyExW                            */
/******************************************************************/

LONG WINAPI Hook_RegCreateKeyExW (HKEY hKey,
				  LPCWSTR lpSubKey,
				  DWORD Reserved,
				  LPWSTR lpClass,
				  DWORD dwOptions,
				  REGSAM samDesired,
				  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
				  PHKEY phkResult,
				  LPDWORD lpdwDisposition)
{
  LPCTSTR	szParentKey ;
  TCHAR		szKeyName[MAX_KEY_PATH] ;
  LONG		lResult ;
  PROC		pfnStub = Hooks_GetStubAddress (HOOKS_REGCREATEKEYEXW) ;

  TRACE ;

  // try to get the full path of this key
  szParentKey = _HookReg_GetKeyPath (hKey) ;
  if( szParentKey )
    {
      wsprintf (szKeyName, TEXT("%s\\%ls"), szParentKey, lpSubKey) ;
      _HookReg_TrimKey (szKeyName) ;
      
      // too many open keys ?
      if( g_pSharedData->nKeys>=MAX_OPEN_KEYS ) {
	TRACE_ERROR (TEXT("Too many open keys, can't open the key \"%s\"\n"),  szKeyName) ;
	return ERROR_TOO_MANY_OPEN_FILES ;
      }
    }

  lResult = (LONG) pfnStub (hKey, lpSubKey, Reserved, lpClass,
			    dwOptions, samDesired, lpSecurityAttributes,
			    phkResult, lpdwDisposition) ;

  if( ERROR_SUCCESS==lResult && szParentKey!=NULL )
    if( ! _HookReg_CanDiscard(szKeyName) )
      _HookReg_AddKey (*phkResult, szKeyName) ;  
  
  return lResult ;
}


/******************************************************************/
/* Exported function : RegCloseKey                                */
/******************************************************************/

LONG WINAPI Hook_RegCloseKey (HKEY hKey) 
{
  LONG		lResult ;
  PROC		pfnStub = Hooks_GetStubAddress (HOOKS_REGCLOSEKEYA) ;

  TRACE ;

  // call real function
  lResult = pfnStub (hKey) ;

  if( ERROR_SUCCESS==lResult ) 
    _HookReg_RemKey (hKey) ;
  
  return lResult ;
}


/******************************************************************/
/* Exported function : RegOpenKeyExA                              */
/******************************************************************/

LONG WINAPI Hook_RegOpenKeyExA (HKEY hKey,
				LPCSTR lpSubKey,
				DWORD dwOptions,
				REGSAM samDesired,
				PHKEY phkResult)
{
  LPCTSTR	szParentKey ;
  TCHAR		szKeyName[MAX_KEY_PATH] ;
  LONG		lResult ;
  PROC		pfnStub = Hooks_GetStubAddress (HOOKS_REGOPENKEYEXA) ;

  TRACE ;

  // try to get the full path of this key
  szParentKey = _HookReg_GetKeyPath (hKey) ;
  if( szParentKey )
    {
      wsprintf (szKeyName, TEXT("%s\\%hs"), szParentKey, lpSubKey) ;
      _HookReg_TrimKey (szKeyName) ;
      
      // too many open keys ?
      if( g_pSharedData->nKeys>=MAX_OPEN_KEYS )
	{
	  TRACE_ERROR (TEXT("Too many open keys, can't open the key \"%s\"\n"),  szKeyName) ;
	  return ERROR_TOO_MANY_OPEN_FILES ;
	}
    }

  // call real RegOpenKeyExA
  lResult = (LONG) pfnStub (hKey, lpSubKey, dwOptions, samDesired, phkResult) ;

  if( ERROR_SUCCESS==lResult && szParentKey!=NULL )
    if( ! _HookReg_CanDiscard(szKeyName) )
      _HookReg_AddKey (*phkResult, szKeyName) ;

  return lResult ;
}


/******************************************************************/
/* Exported function : RegOpenKeyExW                              */
/******************************************************************/

LONG WINAPI Hook_RegOpenKeyExW (HKEY hKey,
				LPCWSTR lpSubKey,
				DWORD dwOptions,
				REGSAM samDesired,
				PHKEY phkResult)
{
  LPCTSTR	szParentKey ;
  TCHAR		szKeyName[MAX_KEY_PATH] ;
  LONG		lResult ;
  PROC		pfnStub = Hooks_GetStubAddress (HOOKS_REGOPENKEYEXW) ;  

  TRACE ;

  // try to get the full path of this key
  szParentKey = _HookReg_GetKeyPath(hKey) ;
  if( szParentKey )
    {
      wsprintf (szKeyName, TEXT("%s\\%ls"), szParentKey, lpSubKey) ;
      _HookReg_TrimKey (szKeyName) ;
      
      // too many open keys ?
      if( g_pSharedData->nKeys>=MAX_OPEN_KEYS ) {
	TRACE_ERROR (TEXT("Too many open keys, can't open the key \"%s\"\n"),  szKeyName) ;
	return ERROR_TOO_MANY_OPEN_FILES ;
      }
    }
 
  // call real RegOpenKeyExW
  lResult = (LONG) pfnStub (hKey, lpSubKey, dwOptions, samDesired, phkResult) ;

  if( ERROR_SUCCESS==lResult && szParentKey!=NULL )
    if( ! _HookReg_CanDiscard(szKeyName) )
      _HookReg_AddKey (*phkResult, szKeyName) ;
 
  return lResult ;
}


/******************************************************************/
/* Exported function : RegSetValueExA                             */
/******************************************************************/

LONG WINAPI Hook_RegSetValueExA (HKEY hKey,
				 LPCSTR lpValueName,
				 DWORD Reserved,
				 DWORD dwType,
				 CONST BYTE *lpData,
				 DWORD cbData) 
{
  PROC	pfnStub = Hooks_GetStubAddress (HOOKS_REGSETVALUEEXA) ;
  
  DWORD		nReaction ;
  LPCTSTR	szKeyPath ;
  TCHAR		*tszValueName ;

  TRACE ;

  szKeyPath = _HookReg_GetKeyPath(hKey) ;
  
  if( szKeyPath )
    {
      // log call
      tszValueName = SpyInt_AsciiToUnicode (lpValueName) ;
      nReaction = SpyInt_CatchCall (REASON_REG_SETVALUE, TEXT("ss"), 
				    szKeyPath, tszValueName) ;
      free (tszValueName) ;
    }
  else
    { 
      nReaction = RULE_ACCEPT ;
    }    

  if( nReaction == RULE_REJECT ) return ERROR_ACCESS_DENIED ; 
  if( nReaction == RULE_FEIGN ) return ERROR_SUCCESS ; 

  return (LONG) pfnStub (hKey, lpValueName, Reserved, dwType, lpData, cbData) ;
}


/******************************************************************/
/* Exported function : RegSetValueExW                             */
/******************************************************************/

LONG WINAPI Hook_RegSetValueExW (HKEY hKey,
				 LPCWSTR lpValueName,
				 DWORD Reserved,
				 DWORD dwType,
				 CONST BYTE *lpData,
				 DWORD cbData) 
{
  LPCTSTR	szKeyPath ;
  PROC	pfnStub = Hooks_GetStubAddress (HOOKS_REGSETVALUEEXW) ;
  DWORD nReaction ;

  TRACE ;
  
  szKeyPath = _HookReg_GetKeyPath(hKey) ;
  
  if( szKeyPath )
    {
      // log call
      nReaction = SpyInt_CatchCall (REASON_REG_SETVALUE, TEXT("ss"), 
				    szKeyPath, lpValueName) ;
    }
  else 
    {
      nReaction = RULE_ACCEPT ;    
    }
      
  if( nReaction == RULE_REJECT ) return ERROR_ACCESS_DENIED ; 
  if( nReaction == RULE_FEIGN ) return ERROR_SUCCESS ; 

  return (LONG) pfnStub (hKey, lpValueName, Reserved, dwType, lpData, cbData) ;
}


UINT HookReg_GetRegKeyList (REGKEYDESC * pKeys, UINT nMaxKeys) 
{
  UINT	nKeys = 0 ;
  UINT	i,iKey,iSlot ;

  _HookReg_LockMutex () ;

  for( i=0 ; i<g_pSharedData->nKeys  ; i++ )
    {
      // look for this key in list
      for( iKey=0 ; iKey<nKeys ; iKey++ )
	if( !_tcsicmp(g_pSharedData->pKeys[i].szPath,pKeys[iKey].szKey) )
	  break ;
      
      // not found ?
      if( iKey>=nKeys ) 
	{
	  // is there enough space
	  if( nKeys>=nMaxKeys )
	    break ;

	  nKeys++ ;
	  
	  pKeys[iKey].nHandles = 0 ;
	  _tcslcpy (pKeys[iKey].szKey, 
		    g_pSharedData->pKeys[i].szPath,
		    MAX_PATH) ;	  
	}

      // count handles
      for( iSlot=0 ; iSlot<HANDLES_PER_KEY ; iSlot++ ) 
	if( g_pSharedData->pKeys[i].aKeys[iSlot] )
	  pKeys[iKey].nHandles++ ;
    }

  _HookReg_UnlockMutex () ;
  
  return nKeys ;
}
